#!/usr/bin/python
# -*- coding: utf-8 -*-
import copy
import os, copy
from GameConfigFile import *


class BSpline(object):
    """Liest B-Spline-Kurven-Dateien aus und berechnet aus ihnen Punkt-Koordinaten.
       Der Knotenvektor wird uniform aus der Anzahl der Punkte berechnet,
       die Ordnung ist fix 4. Beispiel:
       4 Punkte(n+1, da n das Indize ist: B0 bis Bn), Ordnung(k) 4 = Kotenvektor der Länge 8 """

    def __init__(self, file_name="bahnkontrollpunkte.dat", path="data/"):
        self.ROUND = 6 #Gibt an, auf wie viele Stellen bei Punktkoordinaten gerundet wird 
        self.DEGREE = 3 #Grad. Führt bei Änderung zu Fehlern!
        self.__raw_points = self.__get_raw_data(path+file_name)    
        self.coordinates = None #Wird von calculate_coordinates gesetzt
        self.knotvector = self.__calculate_knotvector()
        
    
    def __get_raw_data(self, filename):
        """Erwartet Punktedatei mit 3 Koordinaten je Zeile und gibt sie
        als Liste von Punkten zurueck.
        Vorsicht: Leere Zeilen erzeugen Fehler!"""
        raw_points = [map(float,line.split())
                      for line in file(filename)]

        return raw_points


    def scale_data(self, scale_max = 1):
        """Skaliert die Bahn bis zum Maximalwert scale_max"""

        #Ermittelt den Maximalwert
        maximum = 0
        for i in self.__raw_points:
            for j in i:
                if j > maximum:
                    maximum = j


        #Nimmt die Skalierung vor
        i = -1
        j = -1
        for point in self.__raw_points:
            i += 1
            for coordinate in point:
                j += 1
                self.__raw_points[i][j] /= (maximum/scale_max)
            j = -1
                
                    

    def __calculate_knotvector(self):
        """Errechnet einen Knotenvekor anhand der Kontrollpunkte
           in der Form (0,0,0,0,1,2,...,n-2,n-2,n-2,n-2)
           Gesamtlänge von knotvector sollte n+k+1 sein"""
        knotvector = []
        controllpoints = self.__raw_points
        n = len(controllpoints)-1
        k = self.DEGREE
 
        for i in range(k+1):
            knotvector.append(0)

        for i in range(n-k):
            knotvector.append(i+1)

        for i in range(k+1):
            knotvector.append(n-2)

        return knotvector
               

    def __deboor(self, degree, controllpoints, knotvector, wanted):
        k = degree
        b = controllpoints
        T = knotvector
        t = wanted

        #Berechne r
        r = 0
        while(not(T[r] <= t and t < T[r+1])):
            r +=1

        #Berechne gewünschtes wanted/t
        old_points = copy.deepcopy(b)
        for j in range(1, k+1):
            new_points = range(len(old_points)) 
            for i in range(r-k+j, r+1):
                alpha = (t-T[i])/(T[i-j+k+1]-T[i])
                x = round( (1-alpha)*old_points[i-1][0] + alpha*old_points[i][0],self.ROUND)
                y = round( (1-alpha)*old_points[i-1][1] + alpha*old_points[i][1],self.ROUND)
                z = round( (1-alpha)*old_points[i-1][2] + alpha*old_points[i][2],self.ROUND)
                new_points[i] = [x,y,z]
            old_points = copy.deepcopy(new_points)
        return new_points[r]


    def deboor(self, t):
        """Gibt mit Hilfe des de-Boor-Algorithmus einen Punkt auf einer
           B-Spline-Kurve zurück"""
        degree = self.DEGREE
        controllpoints = self.__raw_points
        return self.__deboor(degree, controllpoints, self.knotvector, t)

    def calculate_points(self, step_range=0.05):
        """Gibt eine List von Koordinaten zurück, die auf der durch die
           Kontrollpunkte definierten Kurve liegen.
           Die Schrittweite zwischen 0 und 1 gibt dabei an, wie viele
           Koordinaten erzeugt werden."""
        T = self.knotvector
        k = self.DEGREE
        n = len(self.__raw_points)-1        


        #Berechnung des Definitionsbereichs
        minimum = T[k]
        maximum = T[n+1]


        #Berechnung der Punkt-Koordinaten
        coordinates = []
        t = minimum
        while(t<maximum):
            coordinates.append(self.deboor(t))
            t += step_range

        #Letzte Koordinate hinzufügen
        coordinates.append(self.__raw_points[-1])

        self.coordinates = coordinates
        return coordinates

        
if __name__=='__main__':
    bspline = BSpline()
    bspline.scale_data()
    #print bspline.deboor(1.0)
    #print bspline.raw_points
    print bspline
    for e in bspline.calculate_points():
        print e
